package cn.csc.framework.common.util.encryption;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Map;
import java.util.TreeMap;

/**
 * @Author：云隐
 * @Date：2023/3/23 15:00
 */
public class RSAUtil {
    private static final String CHARSET = "UTF-8";
    private static final String RSA_ALGORITHM = "RSA";
    /** 针对 keysize = 1024 RSA最大加密明文大小 117 */
    private static final int MAX_ENCRYPT_BLOCK = 117;
    /**  针对 keysize = 1024  RSA最大解密密文大小  128*/
    private static final int MAX_DECRYPT_BLOCK = 128;

//    public final static String PUBLICKEY="MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJ8orgS8vAvwye91I0RSctdhI4zXWK7g\n" +
//            "Y09o6ZQhB8OT2h+aUh4hRzRT6SlCgIO2UCdCVwBBjlvVhlwNLXmCEvkCAwEAAQ==";
//    public final static String PRIKEY="MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAnyiuBLy8C/DJ73Uj\n" +
//            "RFJy12EjjNdYruBjT2jplCEHw5PaH5pSHiFHNFPpKUKAg7ZQJ0JXAEGOW9WGXA0t\n" +
//            "eYIS+QIDAQABAkEAmQs6Ao4l2OmKlHs0FV+e1KKw156/xTE57cM5UFpOp+3BSZjF\n" +
//            "1fDLdPSjsBkzVX6vF4c2rCaH6DqQuNcMj8i9/QIhAMvpMw2HcEdO239jBReLV4lt\n" +
//            "aUeOQ8STf0QltMF95JdHAiEAx9DpVf0NzOuEyMNv954xH0QqtgmQ3tejOPkZerrF\n" +
//            "o78CIAvnd1WI3KMtFV9gdoGjXt7lBkppIceuHLXrxZuuL9QtAiEAkD/xM3KME/Oy\n" +
//            "2Hy+yDQbokbDzsb5HubtnqO2ETPkmmECIFvbt1SC6sNtlLDuwjdPEZ2oM/9FLoES\n" +
//            "Pxjw4sw8yYxx";

    public final static String PUBLICKEY="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsNoyM6FP1o1zKnPgz6wm3MOff\n" +
            "VLxlpRwrx7MAzu/MHbdRqNzb0i0/HwSDuBSLyRA84A3cLNTrCZMheTh7xpF4ZdOI\n" +
            "7mlw1JLzy6Aj+VtP+R1DbzAUjnCTcz+T/gGKpunAmIFqd+AfX9u5eHckQ7H43QKP\n" +
            "3bIimCS0rGYMojC18wIDAQAB";

    public final static String PRIKEY="MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKw2jIzoU/WjXMqc\n" +
            "+DPrCbcw599UvGWlHCvHswDO78wdt1Go3NvSLT8fBIO4FIvJEDzgDdws1OsJkyF5\n" +
            "OHvGkXhl04juaXDUkvPLoCP5W0/5HUNvMBSOcJNzP5P+AYqm6cCYgWp34B9f27l4\n" +
            "dyRDsfjdAo/dsiKYJLSsZgyiMLXzAgMBAAECgYBvmBmT5akib+3dTcJ4wkHVd9eJ\n" +
            "nPtIUqnipNGWIIK7njYAsJAN0jCHyq2+PTbzLiNbj0Tr/BEtDVNOSaMUciQo9Hdf\n" +
            "UHRsrKHA5CFJIg1eKlVh44vXNsNWXSEDHgPDH2g4nT91A59Sq1cFQXO0xFYGJc2d\n" +
            "gP9K3BM/5ErRMwyNQQJBANgJAOQ6HjqOcBb4dvp/Ncu3szzOC8u4pqtUo0ZzYe2I\n" +
            "2gCeZ8r1iYWDB0tHJlSZGY3oMen3yyVU13a1POOH05sCQQDMEjfwmnJZ43KGwZa+\n" +
            "cNl8ZCnVqSxDJi79jBTFTlnk4YojU93KCrzBCuHRy9DwsiOYkxPEV6zPSz8uP201\n" +
            "9OiJAkACA5t5hT0w6JEM9cYOMc9rlcva/kTH4nHTWReomXzJPSRjKxbmooFkuEMC\n" +
            "Jaiq0z6UvxxwBPpk37Sei7QEnwiPAkEAmsHmMkyuE4TtQUxmKR4jkLXVE/crXfED\n" +
            "CbzFs6treKuhupDLTYE/C5ZZEmTimuY9IGPJRcjHgJyr2UK4a4bDIQJAejOZGdT4\n" +
            "APaq8Grs5u1iHaeoqBTidGqGNRs2kKuFD7Cao48yXBPdfC8RWBS8XKjZF13sih17\n" +
            "orpkj0zVvo+XbQ==";

    /**
     * keysize 1024
     * @return
     * @throws NoSuchAlgorithmException
     */
    public static KeyPair createKey() throws NoSuchAlgorithmException {
        KeyPairGenerator generator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
        generator.initialize(1024);
        KeyPair keyPair = generator.generateKeyPair();
        return keyPair;
    }

    /**
     * 得到公钥
     * @param publicKey
     *            密钥字符串（经过base64编码）
     * @throws InvalidKeySpecException
     * @throws NoSuchAlgorithmException
     * @throws Exception
     */
    public static RSAPublicKey getPublicKey(String publicKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.getMimeDecoder().decode(publicKey));
        RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
        return key;
    }

    /**
     * 得到私钥
     *
     * @param privateKey
     *            密钥字符串（经过base64编码）
     * @throws InvalidKeySpecException
     * @throws NoSuchAlgorithmException
     * @throws Exception
     */
    public static RSAPrivateKey getPrivateKey(String privateKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.getMimeDecoder().decode(privateKey));
        RSAPrivateKey key = (RSAPrivateKey)keyFactory.generatePrivate(pkcs8KeySpec);
        return key;
    }

    /**
     * 公钥加密
     *
     * @param data
     *            数据
     * @param publicKey
     *            公钥
     * @return
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws IOException
     * @throws InvalidKeySpecException
     */
    public static String publicEncrypt(String data, String publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        PublicKey pk = getPublicKey(publicKey);
        cipher.init(Cipher.ENCRYPT_MODE, pk);
        byte[] dataByte = data.getBytes(CHARSET), cache;
        int dataLength = dataByte.length, offSet = 0, i = 0;
        try (ByteArrayOutputStream out = new ByteArrayOutputStream();) {
            // 数据分段加密
            while (dataLength - offSet > 0) {
                if (dataLength - offSet > MAX_ENCRYPT_BLOCK) {
                    cache = cipher.doFinal(dataByte, offSet, MAX_ENCRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(dataByte, offSet, dataLength - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_ENCRYPT_BLOCK;
            }
            byte[] encryptData = out.toByteArray();
            return Base64.getMimeEncoder().encodeToString(encryptData);
        }
    }

    /**
     * 私钥解密
     *
     * 解码关键(因为原始数据先geyBytes后再做Base64编码，此处getBytes后再还原回方可解密)
     *
     * @param data
     *            字符串
     * @param privateKey
     *            私钥
     * @return
     * @throws InvalidKeySpecException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws IOException
     */
    public static String privateDecrypt(String data, String privateKey)
            throws Exception {
        PrivateKey pbk = getPrivateKey(privateKey);
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, pbk);
        //Base64做对称解码(一定要先把数据还原，否则解密失败---因为原始数据先geyBytes后再做Base64编码，此处getBytes后再还原回去)
        byte[] dataByte = Base64.getMimeDecoder().decode(data.getBytes()), cache;
        int dataLength = dataByte.length, offSet = 0, i = 0;
        try (ByteArrayOutputStream out = new ByteArrayOutputStream();) {
            // 数据分段解密
            while (dataLength - offSet > 0) {
                if (dataLength - offSet > MAX_DECRYPT_BLOCK) {
                    cache = cipher.doFinal(dataByte, offSet, MAX_DECRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(dataByte, offSet, dataLength - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_DECRYPT_BLOCK;
            }
            byte[] decryptedData = out.toByteArray();
            return new String(decryptedData, CHARSET);
        }
    }

    /**
     * 私钥加密
     *
     * @param data
     *            数据
     * @param privateKey
     *            私钥
     * @return
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws InvalidKeySpecException
     * @throws IOException
     */
    public static String privateEncrypt(String data, String privateKey)
            throws Exception,
            BadPaddingException, InvalidKeySpecException, IOException {
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        PrivateKey pk = getPrivateKey(privateKey);
        cipher.init(Cipher.ENCRYPT_MODE, pk);
        byte[] dataByte = data.getBytes(CHARSET), cache;
        int dataLength = dataByte.length, offSet = 0, i = 0;
        try (ByteArrayOutputStream out = new ByteArrayOutputStream();) {
            // 数据分段加密
            while (dataLength - offSet > 0) {
                if (dataLength - offSet > MAX_ENCRYPT_BLOCK) {
                    cache = cipher.doFinal(dataByte, offSet, MAX_ENCRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(dataByte, offSet, dataLength - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_ENCRYPT_BLOCK;
            }
            byte[] encryptedData = out.toByteArray();
            return Base64.getMimeEncoder().encodeToString(encryptedData);
        }
    }

    /**
     * 公钥解密
     *
     * 解码关键(因为原始数据先geyBytes后再做Base64编码，此处getBytes后再还原回方可解密)
     *
     * @param data
     *            数据
     * @param publicKey
     *            公钥
     * @return
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws InvalidKeySpecException
     * @throws IOException
     */
    public static String publicDecrypt(String data, String publicKey)
            throws Exception {
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        RSAPublicKey rsaPublicKey = getPublicKey(publicKey);
        cipher.init(Cipher.DECRYPT_MODE, rsaPublicKey);
        //Base64做对称解码
        byte[] dataByte = Base64.getMimeDecoder().decode(data.getBytes()), cache;
        int dataLength = dataByte.length, offSet = 0, i = 0;
        try (ByteArrayOutputStream out = new ByteArrayOutputStream();) {
            while (dataLength - offSet > 0) {
                if (dataLength - offSet > MAX_DECRYPT_BLOCK) {
                    cache = cipher.doFinal(dataByte, offSet, MAX_DECRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(dataByte, offSet, dataLength - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_DECRYPT_BLOCK;
            }
            byte[] decryptedData = out.toByteArray();
            return new String(decryptedData, CHARSET);
        }
    }

//    public static void main(String[] args) {
//        TreeMap<String,Object> map = new TreeMap();
//        map.put("channelCode", "W06");
//        map.put("appId",7);
//        map.put("channelId",20);
//        map.put( "merchantOrderId","232463421319131");
//        map.put("subject","商品");
//        map.put("body","商品");
//        map.put("amount",1);
//        map.put("notifyUrl","http://baidu.com");
//        map.put("userIp","127.0.0.1");
//        StringBuffer buffer = new StringBuffer();
//        for(Map.Entry<String,Object> m : map.entrySet()){
//            buffer.append(m.getValue());
//        }
//
//        try {
//            System.out.println(URLEncoder.encode(privateEncrypt(buffer.toString(),PRIKEY),"UTF-8"));
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }
}
